#include "LecteurPhraseAvecArbre.h"

#include <stdlib.h>
#include "Symbole.h"
#include <iostream>
using namespace std;


#include "NoeudVisitor.h"
#include "Valeur.h"
#include <typeinfo>

////////////////////////////////////////////////////////////////////////////////
LecteurPhraseAvecArbre::LecteurPhraseAvecArbre ( string nomFich ) :
        ls ( nomFich ), ts()
{
    nbErreur = 0; // on incrementera si l'on recontre des erreurs dans l'analyse
}

////////////////////////////////////////////////////////////////////////////////
void LecteurPhraseAvecArbre::analyse()
{
    arbre=programme();
    if (nbErreur ==0) {
        cout << "Syntaxe correcte." << endl;
    }
    else {
        cout << "Il y a au moins " << nbErreur << " erreur(s) dans ce programme " << endl;
        throw "syntaxe";
    }
}


////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::programme()
{
// <programme> ::= debut <seqDecl> suite <seqInst> fin FIN_FICHIER
    Noeud* si = NULL;
    Noeud* sd = NULL;
    // --------------------------------------------
    try {
        sauterSymCour ( DEBUT );
    }
    catch (char const*) {
        while (ls.getSymCour() != DEBUT && ls.getSymCour() != "" ) { // si il y a quelque chose avant le DEBUT, on les sautes
            ls.suivant();
        }
        if (ls.getSymCour() == "") {
            cout << "Il manque le \"debut\" du programme " << endl;
            throw "pas debut";
        }
        ls.suivant();
        // sauterSymCour ( DEBUT );
    }
    // --------------------------------------------
    try {
        sd = seqDeclaration();
        sauterSymCour ( SUITE );
    }
    catch (char const*) {
        while (ls.getSymCour() != SUITE  && ls.getSymCour() != "" ) { // si on trouve une erreur, on charche la suite
            ls.suivant();
        }
        if (ls.getSymCour() == "") {
            cout << "Il manque le \"suite\" du programme " << endl;
            throw "pas suite";
        }
        ls.suivant();
        //sauterSymCour ( SUITE );
    }
    // --------------------------------------------
    try {
        si = seqInst();
        sauterSymCour ( FIN ); // a la fin, on abandone si on la trouve pas
    }
    catch (char const*) {
	while (ls.getSymCour() != FIN  && ls.getSymCour() != "" ) { // si on trouve une erreur, on charche la suite
            ls.suivant();
        }
        if (ls.getSymCour() == "") {
            cout << "Il manque le \"fin\" du programme " << endl;
	    throw " pas de fin ";
        }
        ls.suivant();
    }
    // --------------------------------------------
    return sd = new NoeudProg(sd,si);
}


////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::seqDeclaration()
{
// <seqDecl> ::= { <declaration>; }
    NoeudSeqDecl* sd = new NoeudSeqDecl(); // separe les declaration du reste du code
    do
    {
        try {
            sd->ajouteDecl( declaration()); // on ajoute les declaration a la sequence
            sauterSymCour ( ";" ); // elle douvent toute se terminer par un ;
        }
        catch (char const*) {
            while (ls.getSymCour() != ";" && ls.getSymCour() != "") { // on cherche une autre declaration, celle ci etant invalide.
                ls.suivant();
            }
            ls.suivant();
            nbErreur++; // on compte le nombre d'erreur
        }
    }
    while ( (((ls.getSymCour() =="<VARIABLE>" ) || (ls.getSymCour() ==ENTIER ) || ( ls.getSymCour() ==REEL ) ||
              ( ls.getSymCour() ==CHAR ) || ( ls.getSymCour() ==CHAINE )) || (ls.getSymCour() != SUITE) )  && ls.getSymCour() != "" );
    return sd;
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::declaration()
{
    // <declaration> ::= int | float | string | char <variable>
    try {
        string type = CHAINE;
        Noeud* var = NULL;
        if (ls.getSymCour() == "<VARIABLE>") {
            erreurSymboleInconu(); // si on a pas Entier, Reel, chaine ou char, c'est pas correct
        }
        if ( ls.getSymCour() ==ENTIER ) { // on evalut le type du symbole courant
            type = ENTIER;
        }
        if ( ls.getSymCour() ==REEL) {  // on evalut le type du symbole courant
            type = REEL;
        }
        if (ls.getSymCour() ==CHAINE) { // on evalut le type du symbole courant
            type = CHAINE;
        }
        if ( ls.getSymCour() ==CHAR ) { // on evalut le type du symbole courant
            type = CHAR;
        }
        ls.suivant();
        if ( ! ( ts.variableExiste ( ls.getSymCour() ) ) )
        {
            var = ts.chercheAjoute ( ls.getSymCour(),type,ls.getLigne() ); // si la variable n'existe pas, on l'enregistre dans Table Sym
            ls.suivant();
        }
        else {
            erreurVarExistDeja ( type); // sinon, on leve une exception
        }
        return new NoeudDeclaration ( var,type ); // on cree l'arbre
    }
    catch (char const*) {
        throw "decl";
    }
}



////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::seqInst()
{
// <seqInst> ::= <inst> ; { <inst> ; }
    NoeudSeqInst* si = new NoeudSeqInst();
    do // on a au moins une instruction
    {
        try {
            si->ajouteInstruction (inst()); // on creer l'instruction
            sauterSymCour ( ";" );
        }
        catch (char const* st) {
            while (ls.getSymCour() != ";" && ls.getSymCour() != "") { // on cherche une autre instruction, celle ci etant invalide.
                ls.suivant();
            }
            ls.suivant();
            nbErreur++; // on compte le nombre d'erreur
        }
    }
    while ( (( ls.getSymCour() =="<VARIABLE>" ) || ( ls.getSymCour() ==TANTQUE ) ||
             ( ls.getSymCour() ==REPETER ) || ( ls.getSymCour() ==SI ) ||
             ( ls.getSymCour() ==POUR ) || ( ls.getSymCour() ==LIRE ) ||
             ( ls.getSymCour() ==ECRIRE ) || ( ls.getSymCour() ==ENTIER ) ||
             ( ls.getSymCour() ==REEL ) || ( ls.getSymCour() ==CHAR ) ||
             ( ls.getSymCour() ==CHAINE )) && ls.getSymCour() != "" );
    return si;
}


////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::inst()
//    <inst> ::= <affectation> | <instSi> | <instTq> | <instRepeter> | <instPour> | <instLire> | <instEcrire>
{
    try {
        if ( ls.getSymCour() =="<VARIABLE>" )
            return affectation();
        else if ( ls.getSymCour() ==ENTIER || ls.getSymCour() ==REEL || ls.getSymCour() ==CHAINE || ls.getSymCour() ==CHAR ) { // on lance la "conditionr qui contiendra elle-meme des instructions. (condition = ExpBool)
            erreurPlaceDeclaration();
        }
        else if ( ls.getSymCour() ==SI ) // on lance la "conditionr qui contiendra elle-meme des instructions. (condition = ExpBool)
            return instSi();
        else if ( ls.getSymCour() ==TANTQUE ) // idem que if
            return instTq();
        else if ( ls.getSymCour() ==REPETER ) // idem que if
            return instRepeter();
        else if ( ls.getSymCour() ==POUR ) // idem que if
            return instPour();
        else if ( ls.getSymCour() ==LIRE ) // instruction pour lire une variable au clavier. (accepte que des <VARIABLE>)
            return instLire();
        else if ( ls.getSymCour() ==ECRIRE ) // instruction pour ecrire a l'ecran des variable, entier, chaine.
            return instEcrire();
        else
            erreur ( " une instruction " ); // si on a rien de tout ce qu'il y a au dessus, c'est incorrect
        return NULL;
    }
    catch (char const*) {
        throw;
    }
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::instSi()
//  <instSi> ::= si ( <expBoolOu> ) <seqInst> { sinonsi ( <expBoolOu> ) <seqInst> } [ sinon <seqInst> ] finsi
{
    Noeud* suivant = NULL;
    Noeud* exp = NULL;
    Noeud* seqinst = NULL;
    Noeud* si = NULL;
    if ( ls.getSymCour() ==SI )
    {
        try {
            sauterSymCour ( SI );
            sauterSymCour ( "(" );
            exp = expBoolOu();
            sauterSymCour ( ")" );
            seqinst = seqInst();
            suivant = instSi();
            si = new NoeudSi ( exp,seqinst,suivant );
            sauterSymCour(FINSI);
        }
        catch (char const*) {
            sauterSeqInst(FINSI);
            throw SI;
        }
    }
    else if ( ls.getSymCour() ==SINONSI )
    {
        try {
            sauterSymCour ( SINONSI );
            sauterSymCour ( "(" );
            exp = expBoolOu();
            sauterSymCour ( ")" );
            seqinst = seqInst();
            suivant = instSi();
            si = new NoeudSi ( exp,seqinst,suivant );
        }
        catch (char const*) {
            sauterSeqInst(FINSI);
            throw SI;
        }
    }
    else if ( ls.getSymCour() ==SINON )
    {
        try {
            ls.suivant();
            seqinst = seqInst();
            exp = ts.chercheAjoute ( Symbole ( "1" ) ); // toujour vrai
            si = new NoeudSi ( exp,seqinst,suivant ); // ici, le suivant sera null
        }
        catch (char const*) {
            sauterSeqInst(FINSI);
            throw SI;
        }
    }
    return si; // a la fin, on retourne le si (si tout c'est bien passé)

}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::instTq()
// <instTq> ::= tantque ( <expBool> ) <seqInst> fintantque
{
    try {
        sauterSymCour ( TANTQUE );
        sauterSymCour ( "(" );
        Noeud* exp = expBoolOu();
        sauterSymCour ( ")" );
        Noeud* seqinst = seqInst();
        sauterSymCour ( FINTANTQUE );
        return new NoeudTantQue ( exp,seqinst );
    }
    catch (char const*) {
        sauterSeqInst(FINTANTQUE);
        throw TANTQUE;
    }
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::instPour()
// <instPour> ::= pour ( affectation ; <expBool>; affectation ) <seqInst> finpour
{
    try {
        sauterSymCour ( POUR );
        sauterSymCour ( "(" );
        Noeud* aff = affectation(true);
        sauterSymCour ( ";" );
        Noeud* exp = expBoolOu();
        sauterSymCour ( ";" );
        Noeud* aff2 = affectation(true);
        sauterSymCour ( ")" );
        Noeud* seqinst = seqInst();
        sauterSymCour ( FINPOUR );
        return new NoeudPour ( aff,aff2,exp,seqinst );
    }
    catch (char const*) {
        sauterSeqInst(FINPOUR);
        throw POUR;
    }
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::instRepeter()
// <instRepeter> ::= repeter <seqInst> jusqua ( <expBool> )
{
    try {
        sauterSymCour ( REPETER );
        Noeud* seqinst = seqInst();
        sauterSymCour ( JUSQUA );
        sauterSymCour ( "(" );
        Noeud* exp = expBoolOu();
        sauterSymCour ( ")" );
        return new NoeudRepeter ( exp,seqinst );
    }
    catch (char const*) {
        sauterSeqInst(JUSQUA);
        throw REPETER;
    }
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::instLire() //<instLire> ::= lire ( <variable> )
{
    try {
        Noeud* fact = NULL;
        sauterSymCour ( LIRE );
        sauterSymCour ( "(" );
        if ( ls.getSymCour() =="<VARIABLE>" ) // on ne peut lire que dans des variables
        {
            fact = facteur();
            sauterSymCour ( ")" );
        }
        else
            erreur ( "<variable>" );
        return new NoeudLire ( fact );
    }
    catch (char const*) {
        throw LIRE;
    }
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::instEcrire() // <instEcrire> ::= ecrire ( <expression> | <chaine> )
{
    try {
        sauterSymCour ( ECRIRE );
        sauterSymCour ( "(" );
        Noeud* exp = expression();
        sauterSymCour ( ")" );
        return new NoeudEcrire ( exp );
    }
    catch (char const*) {
        throw ECRIRE;
    }
}


////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::affectation(bool pour)
{
// <affectation> ::= <variable> = <relation>
    try {
        Noeud* var = NULL;
        Noeud* exp = NULL;
        testerSymCour ( "<VARIABLE>" );
        if ( ts.variableExiste ( ls.getSymCour() ) )
        {
            var = ts.chercheAjoute ( ls.getSymCour(),ls.getLigne() );
            ls.suivant();
            sauterSymCour ( "=" );
            exp = relation(); // on n'accepte pas les booleens
        }
        else {
            erreurSymboleInconu ();
        }
        if (pour)
            return new NoeudAffectationPour ( var,exp );
        else
            return new NoeudAffectation ( var,exp );
    }
    catch (char const*) {
        throw "aff";
    }
}


////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::expBoolOu()
// <expBoolOu> ::= <expBoolEt> { <opOu> <expBoolEt> }
{
    try {
        Noeud* rel = expBoolEt();
        while ( ls.getSymCour() ==OU )
        {
            Symbole operateur = opOu();
            Noeud* relDroit = expBoolEt();
            rel = new NoeudOperateurBinaire ( operateur,rel,relDroit ); // const. du noeud
        }
        return rel;
    }
    catch (char const*) {
        throw;
    }
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::expBoolEt()
// <expBoolEt> ::= <relation> { <opEt> <relation> }
{
    try {
        Noeud* rel = relation();
        while ( ls.getSymCour() ==ET )
        {
            Symbole operateur = opEt();
            Noeud* relDroit = relation();
            rel = new NoeudOperateurBinaire ( operateur,rel,relDroit ); // const. du noeud
        }
        return rel;
    }
    catch (char const*) {
        throw;
    }
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::relation()
// <relation> ::= <expression> { <opRel> <expression> }
{
    try {
        Noeud* exp = expression();

        while ( ls.getSymCour() =="==" || ls.getSymCour() =="!=" ||
                ls.getSymCour() =="<" || ls.getSymCour() =="<=" ||
                ls.getSymCour() ==">" || ls.getSymCour() ==">=" )
        {
            Symbole operateur = opRel();
            Noeud* expDroit = expression();
            exp = new NoeudOperateurBinaire ( operateur,exp,expDroit ); // const. du noeud
        }
        return exp;
    }
    catch (char const*) {
        throw;
    }
}


////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::expression()
{
// <expression> ::= <terme> { <opAdd> <terme> }
    try {
        Noeud* term = terme();
        while ( ls.getSymCour() =="+" || ls.getSymCour() =="-" )
        {
            Symbole operateur =opAdd();
            Noeud* termDroit = terme();
            term = new NoeudOperateurBinaire ( operateur,term,termDroit ); // const. du noeud
        }
        return term;
    }
    catch (char const*) {
        throw;
    }
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::terme()
{
// <terme> ::= <facteur> { <opMult> <facteur> }
    try {
        Noeud* fact = facteur();
        while ( ls.getSymCour() =="*" || ls.getSymCour() =="/" )
        {
            Symbole operateur =opMult(); // on stocke le symbole de l'opérateur
            Noeud* factDroit=facteur(); // lecture de l'operande droit
            fact = new NoeudOperateurBinaire ( operateur,fact,factDroit ); // const. du noeud
        }
        return fact;
    }
    catch (char const*) {
        throw;
    }
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::facteur()
{
// <facteur> ::= <entier> | <reel> | <chaine> | <char> | <variable> | <opUnaire> <expBoolOu> | ( <expBoolOu> )
    try {
        Noeud* fact=NULL;
        if ( ls.getSymCour() =="<ENTIER>"  || ls.getSymCour() =="<REEL>" || ls.getSymCour() =="<CHAINE>" || ls.getSymCour() =="<CARACT>" )
        {
            fact=ts.chercheAjoute ( ls.getSymCour(),ls.getLigne() );
            ls.suivant();
        }
        else if ( ls.getSymCour() =="<VARIABLE>")
        {
            if (! ts.variableExiste ( ls.getSymCour() ) )
                erreurSymboleInconu();
            else
                fact=ts.chercheAjoute ( ls.getSymCour(),ls.getLigne() );
            ls.suivant();
        }
        else if ( ls.getSymCour() =="-" )
        {
            ls.suivant();
            fact = new NoeudOperateurBinaire ( Symbole ( "-" ), ts.chercheAjoute ( Symbole ( "0" ) ), facteur());
        }
        else if ( ls.getSymCour() ==NON )
        {
            ls.suivant();
            fact= ts.chercheAjoute ( Symbole ( "1" ),ls.getLigne() );
            fact = new NoeudOperateurBinaire ( Symbole ( "!" ), fact, facteur() );
        }
        else if ( ls.getSymCour() =="(" )
        {
            ls.suivant();
            fact=expBoolOu();
            sauterSymCour ( ")" );
        }
        else
            erreur ( " un facteur " );
        return fact;
    }
    catch (char const*) {
        throw;
    }
}


////////////////////////////////////////////////////////////////////////////////
Symbole LecteurPhraseAvecArbre::opEt()
// <opEt> ::= et
{
    try {
        Symbole operateur;
        if ( ls.getSymCour() ==ET )
        {
            operateur=ls.getSymCour();
            ls.suivant();
        }
        else
            erreur ( " un \"et\" booleen" );
        return operateur;
    }
    catch (char const*) {
        throw;
    }
}

////////////////////////////////////////////////////////////////////////////////
Symbole LecteurPhraseAvecArbre::opOu()
// <opOu> ::= ou
{
    try {
        Symbole operateur;
        if ( ls.getSymCour() ==OU )
        {
            operateur=ls.getSymCour();
            ls.suivant();
        }
        else
            erreur ( " un \"ou\" booleen" );
        return operateur;
    }
    catch (char const*) {
        throw;
    }
}

////////////////////////////////////////////////////////////////////////////////
Symbole LecteurPhraseAvecArbre::opRel()
// <opRel> ::= == | != | < | <= | > | >=
{
    try {
        Symbole operateur;
        if ( ls.getSymCour() =="==" || ls.getSymCour() =="!=" ||
                ls.getSymCour() =="<" || ls.getSymCour() =="<=" ||
                ls.getSymCour() ==">" || ls.getSymCour() ==">=" )
        {
            operateur=ls.getSymCour();
            ls.suivant();
        }
        else
            erreur ( "un comparateur `\"==, !=, <=, >=, <, >\" " );
        return operateur;
    }
    catch (char const*) {
        throw;
    }
}

////////////////////////////////////////////////////////////////////////////////
Symbole LecteurPhraseAvecArbre::opUnaire()
// <opUnaire> ::= - | non
{
    try {
        Symbole operateur;
        if ( ls.getSymCour() =="-" || ls.getSymCour() ==NON )
        {
            operateur=ls.getSymCour();
            ls.suivant();
        }
        else
            erreur ( "un \"-\" ou un \"!\" " );
        return operateur;
    }
    catch (char const*) {
        throw;
    }
}


////////////////////////////////////////////////////////////////////////////////
Symbole LecteurPhraseAvecArbre::opAdd()
// <opAdd> ::= + | -
{
    try {
        Symbole operateur;
        if ( ls.getSymCour() =="+" || ls.getSymCour() =="-" )
        {
            operateur=ls.getSymCour();
            ls.suivant();
        }
        else
            erreur ( "un operateur d'addition \"+,-\" ");
        return operateur;
    }
    catch (char const*) {
        throw;
    }
}

////////////////////////////////////////////////////////////////////////////////
Symbole LecteurPhraseAvecArbre::opMult()
// <opMult> ::= *  | /
{
    try {
        Symbole operateur;
        if ( ls.getSymCour() =="*" || ls.getSymCour() =="/" )
        {
            operateur=ls.getSymCour();
            ls.suivant();
        }
        else
            erreur ( "un operateur de multiplication \"*,/\" " );
        return operateur;
    }
    catch (char const*) {
        throw;
    }
}


////////////////////////////////////////////////////////////////////////////////
void LecteurPhraseAvecArbre::testerSymCour ( string ch )
{
    try {
        if ( ls.getSymCour() != ch )
        {
            cout << endl << "-------- Erreur ligne " << ls.getLigne() << " - Colonne " << ls.getColonne()
                 << endl << "   Attendu : \"" << ch << "\" ;  Trouve  : " << ls.getSymCour() << endl
                 << endl;
            throw "symbole"; // plus tard, on levera une exception
        }
    }
    catch (char const*) {
        throw;
    }
}

////////////////////////////////////////////////////////////////////////////////
void LecteurPhraseAvecArbre::erreur ( string mess )
{
    try {
        cout << endl << "-------- Erreur ligne " << ls.getLigne() << " - Colonne " << ls.getColonne()
             << endl << "   Attendu : \"" << mess << "\" ;  Trouve  : " << ls.getSymCour() << endl << endl;
        throw "symbole";
    }
    catch (char const*) {
        throw;
    }
}

void LecteurPhraseAvecArbre::erreurPlaceDeclaration () {
    try {
        cout << endl << "-------- Erreur ligne " << ls.getLigne() << " - Colonne " << ls.getColonne() << endl
             << "   La declaration des variables se fait dans le bloc debut-suite"  << endl << endl;
        throw "symbole";
    }
    catch (char const*) {
        throw;
    }
}

void LecteurPhraseAvecArbre::erreurVarExistDeja (string type) {
    try {
        cout << endl << "-------- Erreur ligne " << ls.getLigne() << " - Colonne " << ls.getColonne() << endl
             << "   La variable : \"" << ls.getSymCour().getChaine() << "\" de type " << type << " existe deja." << endl << endl;
        throw "symbole";
    }
    catch (char const*) {
        throw;
    }
}

void LecteurPhraseAvecArbre::erreurSymboleInconu () {
    try {
        cout << endl << "-------- Erreur ligne " << ls.getLigne() << " - Colonne " << ls.getColonne() << endl
             << "   Le Symbole : \"" << ls.getSymCour().getChaine() << "\" est inconnue." << endl << endl;
        throw "symbole";
    }
    catch (char const*) {
        throw;
    }
}

////////////////////////////////////////////////////////////////////////////////
void LecteurPhraseAvecArbre::sauterSymCour ( string ch )
{
    try {
        testerSymCour ( ch );
        ls.suivant();
    }
    catch (char const*) {
        throw;
    }
}


void LecteurPhraseAvecArbre::sauterSeqInst(string ch)
{
    int instPour = 0;
    int instRepeter = 0;
    int instSi = 0;
    int instTq = 0;
    try {
        while ((ls.getSymCour() != ch && instPour !=0 && instRepeter != 0 && instSi != 0 && instTq != 0) || ((ls.getSymCour()!="fin") && (ls.getSymCour()!=""))) {
            if (ls.getSymCour() == POUR)
                instPour++;
            if (ls.getSymCour() == FINPOUR)
                instPour--;
            if (ls.getSymCour() == REPETER)
                instRepeter++;
            if (ls.getSymCour() == JUSQUA)
                instRepeter--;
            if (ls.getSymCour() == SI)
                instSi++;
            if (ls.getSymCour() == FINSI)
                instSi--;
            if (ls.getSymCour() == TANTQUE)
                instTq++;
            if (ls.getSymCour() == FINTANTQUE)
                instTq--;
            ls.suivant();
        }
    }
    catch (char const*) {
        throw;
    }
}

